package com.univrz.flexmachine.scxml.model
{
	
	
	import flash.events.EventDispatcher;
	
	
	
	public class Node extends SCXMLModelObject
	{
	
	
	
	

		public var children:Array = [];
		
		public var parent:Node;
		
			


		public var onentry:*;
	
		public var onexit:*;	
	
	
	
	
		/**
		 * 
		 * @param parent
		 * @param onentry
		 * @param onexit
		 * 
		 */
		public function Node( parent:Node = null, onentry:*=undefined, onexit:*=undefined )
		{
			this.parent = parent;
			
			if ( parent )
				parent.children.push( this );
				
			this.onentry = onentry;
			this.onexit = onexit;
				
		}



		
		/**
		 * 
		 * A state of type <state> with no child states, or a state of type <final>.
		 * 
		 * @return 
		 * 
		 */				
		public function get isAtomic( ):Boolean
		{
			if ( this is FinalNode )
				return true;
			
			if ( this is StateNode )
				return children.length == 0;
				
			return false;
			
		}

		


		/**
		 * 
		 * Outgoing Transitions
		 * 
		 * @return 
		 * 
		 */
		public function get transitions( ):Array
		{
			return TransitionEdge.forSource( this );
		}







	
		public var isActive:Boolean;	
			
			
			
			
		public function enter( ):void
		{
			
						
		
		}


		
		public function exit( ):void
		{
			if ( !isActive )
				return;
			// tell our children to exit first
			for each ( var child:Node in children )
			{
				child.exit( );
			}
			isActive = false;
			doExitBusiness( );
		}			
			
			
		protected function doExitBusiness( ):void
		{

		}
			
		protected function doEnterBusiness( ):void
		{
		
		
		}
		
		
			
			
			
			
			
			
			
			
			
			
		////////////////////////////////////////////////////////////////
		// getChildrenOfType 
		////////////////////////////////////////////////////////////////		
		
		public function getChildrenOfType( type:Class ):Array
		{
			var result:Array = [];
			for each ( var child:Object in children )
				if ( child is type )
					result.push( child );
			return result;
		}
		
				
				
				
		////////////////////////////////////////////////////////////////
		// ancestors
		////////////////////////////////////////////////////////////////
		
		private var _ancestors:Array;
		
		public function get ancestors( ):Array
		{
			if ( !_ancestors )
			{
				_ancestors = [];
				if ( parent != null )
					_ancestors = _ancestors.concat( parent.ancestors );			
			}
			return _ancestors;
		}
		
		
		
		public function get ancestorsIncludingThis( ):Array
		{
			return [this].concat( ancestors );
		}
		
		
		
		
		////////////////////////////////////////////////////////////////
		// descendants
		////////////////////////////////////////////////////////////////		
		
		private var _descendants:Array;
		
		public function get descendants( ):Array
		{
			if ( !_descendants )
			{
				_descendants = [];
				for each ( var child:Node in children )
				{
					_descendants.push( child );
					_descendants = _descendants.concat( child.descendants );			
				}
			}
			return _descendants;
		}		
		
		


		////////////////////////////////////////////////////////////////
		// getLCA
		////////////////////////////////////////////////////////////////

//		/**
//		 * 
//		 * Returns the least common ancestor between this node and the other, if any
//		 * 
//		 * @param otherNode
//		 * @return 
//		 * 
//		 */		
//		public function getLCA( otherNode:Node ):Node
//		{		
//			if ( _getLCA[otherNode] === undefined )
//			{
//				var n:Node = $getLCA( otherNode );
//				_getLCA[otherNode] = n;
//				return n;
//			}
//			return _getLCA[otherNode];
//		}
//		
//		private var _getLCA:Dictionary = new Dictionary( ); // why make it weak if the complete state machine works as a block 
//				 
//		private function $getLCA( otherNode:Node ):Node
//		{
//		
//			if ( this == otherNode )	// border case
//				return this;
//			
//			var acs:Array = [this].concat( ancestors );	// our ancestors including us
//		
//			var p:Node = otherNode;
//
//			do
//			{
//				if ( acs.indexOf( p ) != -1 )
//					return p;			
//			}
//			while ( p = otherNode.parent )
//			
//			return null;
//		
//		}
		
		
		
		////////////////////////////////////////////////////////////////
		// isDescendantOf
		////////////////////////////////////////////////////////////////		

		/**
		 * 
		 * @param possibleAncestor
		 * @return 
		 * 
		 */		
		public function isDescendantOf( possibleAncestor:Node ):Boolean
		{
			return ( ancestors.indexOf( possibleAncestor ) != -1 );
		}
								
		
		
		
		
		
		
		/**
		 * 
		 * Returns the Least Common Ancestor between a set of nodes
		 * 
		 * TODO: caching
		 * 
		 * @param nodes
		 * @return 
		 * 
		 */		
		public static function findLCA( nodes:Array ):Node
		{
			
			if ( nodes.length == 1 )
				return nodes[0].parent;
		
			var acs:Array = nodes.map( 
									function( node:Node, index:int, array:Array ):Array {
										return node.ancestors;
									});
			
			
			var ps:Array = acs.shift( );
							
			for each ( var p:Node in ps )
			{
				if ( acs.every(
						function( arr:Array, index:int, array:Array ):Boolean
						{
							return ( arr.indexOf( p ) != -1 );
						}
					 ) )
					return p;
			}						
				
			return null;
		}
		
		
		
		
		internal function get engine( ):SCXMLCoreInterpreter
		{
		
			return null;
		}
		
		
		
		
		
		
		
		
		
		internal static function filter_isActive( node:Node, index:int, array:Array ):Boolean
		{
			return node.isActive;		
		}
		
			

	}
	
}